This tutorial is a derivative (simplification really) of work done by JBoss. For the original material, look here. This tutorial's goals are:
Download and install the JBoss Embeddable EJB3 Container
Create and configure an Eclipse project to be able to use the container
Create a simple "hello world" style local stateless session bean
Look up that local stateless session bean and use it
Most of this tutorial is about configuration and utility methods. We will use those items for the remainder of the tutorials using the Embeddable EJB3 Container.
Background
A Session Bean provides an interface to a service, wraps business logic or might simply act to hide the details of using JPA. The design pattern that comes to mind is facade.
Creating a Session Bean using EJB3 is comparatively painless compared to previous versions of the EJB specification. In the simplest configuration, a session bean only needs to implement an interface and then be denoted as a session bean with meta-information. EJB3 uses configuration by exception to keep the requirements to a minimum.
EJB meta-information comes in two forms: Annotations or XML files. As with JPA, XML files take precedence over annotations. These tutorials use annotations.
Download and Install
This tutorial assumes you already have Eclipse 3.2 and a Java 5 JDK installed. If not, please review this.
Download
Download the JBoss EJB3 Embeddable Container from here. The version used for this tutorial is jboss-EJB-3.0_Embeddable_ALPHA_9.zip.
Install
Extract this zip file anywhere, for this tutorial I used C:\libs, which makes the following directory: C:\libs\jboss-EJB-3.0_Embeddable_ALPHA_9
You'll find the following directories under the install directory:
conf
docs
lib
We'll be using the files from conf and lib.
Create and Configure the Eclipse Project
Define User Library
We need to add several jar files to the class path. We created a user library in these steps and we're going to do the same thing with a different set of jar files.
Create User Library
Pull down Window:Preferences
Navigate to Java:Build Path:User Libraries
Click on New
Enter EJB3_EMBEDDABLE for the name and click on OK
Add Jars to User Library
Now we need to add all of the jar files from the lib directory under the JBoss Embeddable container directory we unzipped earlier:
Select EJB3_EMBEDDABLE
Click on Add JARs...
Navigate to C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/lib/
Select all of the the jar files
Click on Open
You need to add all of the jar files from the lib directory under the JBoss Embeddable container directory. If you used the same directory as the tutorial, then you need add all of the jar files from C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/lib/ (there are 5). Here is the complete list of jar files:
For the rests of this tutorial, when you see <project> replace it with Ejb3Tutorial1.
Create the Project
Pull down the File menu and select new:project
Select Java Project
Click Next
Enter <project>
Under Project Layout select create separate source and output folders
Click Finish
Select <project>, right-click and select new:Source Folder
Enter conf for the name
Click on Finish
Select <project>, right-click and select new:Source Folder
Enter test for the name
Click on Finish
Edit your project properties
Now that we have created a user library, we can add that user library to our project:
Select <project>, and press alt-enter or right-click and select properties.
Select Java Build Path
Select the Libraries tab
Click on Add Library
Select User Library and click Next
Click on the box next to EJB3_EMBEDDABLE and click Finish
Click Add Library
Select JUnit and click Next
In the pull-down list, select JUnit 4 and click Finish
Click on OK to make the change to your project's classpath
Setup the configuration files
The JBoss Embeddable container looks for several files in the classpath. To keep all of these in one place, we'll add another source directory to our project and then import several files into that directory.
Select the conf folder under <project>
Pull down the File menu and select Import
Expand General
Select File System and click on Next
Click on Browse and go to the following directory: C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/conf
Click on OK
You'll see conf in the left pane, select it
Verify that the Into folder: lists <project>/conf (if not, just enter it or browse to it)
Click Finish
Expand the conf directory and verify that the files are now there
Add Resource Adapter Archive(RAR)
The Java Connector system defines Resource Adapter Archive files (RAR files). We need to add a few RAR files into the class path. We will import two more files into the conf directory:
Select the conf folder
Pull down the File menu and select Import
Expand General
Select File System and click on Next
Click on Browse and go to the following directory: C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/lib
Select jcainflow.rar and jms-ra.rar
Click Finish
Create a jndi.properties file
Note, depending on the version of the embeddable container you download, you might already have a file called jndi.properties. If you do, skip to the next section.
Select the conf directory, right-click and select new then select File
Enter the name jndi.properties and click finish
Enter the following 2 lines then save and close the file:
This example presents a utility class we'll be using later. The container needs a persistence.xml file to operate. This file must be found under a META-INF directory somewhere in the classpath or the embeddable container will not start. The file's name is persistence.xml with a lower-case 'p'. On a Unix system, this will make a difference. On a PC, this won't make a difference and it is one of those things that might work on your machine but not on the linux build box.
Select your src directory
Right-click, select New:Folder
Enter META-INF
Click OK
Select META-INF
Right-lick, select New:File
Enter persistence.xml
Click Finish
Copy the following example into your new file then save it by pressing ctrl-s
persistence.xml
<?xmlversion="1.0"encoding="UTF-8"?><persistence><persistence-unitname="custdb"><!-- This persistence unit uses the default data source that JBoss --><!-- defines called DefaultDS. If we wanted to use our own data --><!-- source, we'd need to define a custom data source somewhere. --><!-- That somewhere is vendor specific. --><!-- In the case of JBoss, since we're using the embedded container, --><!-- we need to add two beans in a file called --><!-- embedded-jboss-beans.xml. We name the first --><!-- HypersonicLocalServerDSBootstrap and we name the second --><!-- HypersonicLocalServerDS. This two step process defines a data --><!-- source. --><!-- In the first bean definition, we additionally bind it in Jndi --><!-- under some name. If we used the name --><!-- java:/HypersonicLocalServerDS then we would use the following --><!-- entry to use that data source instead of the default one: --><!-- <jta-data-source>java:/HypersonicLocalServerDS</jta-data-source> --><jta-data-source>java:/DefaultDS</jta-data-source><properties><propertyname="hibernate.hbm2ddl.auto"value="create-drop"/></properties></persistence-unit></persistence>
Create a Local Stateless Session Bean
Now that we have all the preliminary setup of the environment we next need to create a session bean.
The Session Bean
The basic requirement for a session bean is that it must implement an interface and be either annotated with @Stateless or be registered in an XML file. We are sticking strictly to using annotations. The annotation goes on the class and not the interface, so here's the interface.
The Interface
First we create a session bean. Here is one such example:
packageservice;/**
* A requirement for EJB3 Session beans is that they implement an interface.
* This interface does not specify whether it is local or remote so we won't
* know until it is used which way to treat it. One convention is to include the
* name "Local" or "Remote" in the name of the service.
*/publicinterface HelloWorldService {void sayHelloTo(finalString name);}
To create this file,
select your src directory, right-click and select New:Interface.
For Name, enter HelloWorldService
For Package enter service
Click on Finish then enter the above code into the file
Save your file (ctrl-s)
The Session Bean
Next, we need to create a session bean. Here's the code for it:
packageservice.impl;importjavax.ejb.Stateless;importservice.HelloWorldService;/**
* I'm a stateless session because of the -at- Stateless annotation. I only
* implement one interface and that interface does not explicitly declare itself
* to be either -at- Local or -at- Remote. Therefore, I am a Stateless session
* bean with a local interface.
*/
@Stateless
publicclass HelloWorldServiceImpl implements HelloWorldService {publicvoid sayHelloTo(finalString name){System.out.printf("Hello to %s\n", name);}}
Notice that this class has the @Stateless annotation. The container will find this class and register it automatically under JNDI using the (non-package qualifited) class name plus "/local". In this example, that means we'll need to lookup "HelloWorldServiceImpl/local".
This class is obviously stateless because of the annotation. This is the default behavior. However, using the annotation will get it automatically available from JNDI. (We could put this information in an XML file and get the same results.)
This class is also local. By default, session beans are local (no RMI-IIOP to call them) unless:
They implement more that one interface (ignoring common interfaces like Serializable and Comparable).
There is a @Remote annotation
If you still want a local session bean where there is more than one interface, you can use @Local.
To create this file:
select your src directory, right-click and select New:Class
For Name, enter HelloWorldServiceImpl
For Package enter service.impl
Click on Finish then enter the above code into the file
Save your file (ctrl-s)
Container Initialization and Session Bean Lookup
There's a bit of boilerplate code to get the JBoss EJB3 Embeddable Container initialized before we can look up our session bean. For now we'll just use this code so we won't describe it in any detail here.
By the way, I recommend you cut and past this code rather than type it.
JBossUtil.java
packageutil;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.OutputStream;importjava.io.PrintStream;importjava.util.logging.Logger;importjavax.naming.InitialContext;importjavax.naming.NamingException;importorg.jboss.ejb3.embedded.EJB3StandaloneBootstrap;/**
* This class was originally necessary when using the ALPHA 5 version of the
* embeddable container. With the alpha 9 release, initialization is quite
* simple, you need just 2 lines to initialize your JBoss Embeddable EJB3
* Container Environment. Unfortunately, the one that is available for download
* uses System.out.println() in a few places, so this simple utility hides that
* output and also provides a simple lookup mechanism.
*/publicclass JBossUtil {privatestaticPrintStream originalOut;privatestaticPrintStream originalErr;privatestaticOutputStream testOutputStream;privatestaticPrintStream testOuputPrintStream;staticboolean initialized;staticInitialContext ctx;private JBossUtil(){// I'm a utility class, do not instantiate me}/**
* JBoss EJB3 Embeddable Container uses System.out. Redirect that output to
* keep the console output clean.
*/privatestaticvoid redirectStreams(){// configure the console to get rid of hard-coded System.out's in// the JBoss libraries
testOutputStream = newByteArrayOutputStream(2048);
testOuputPrintStream = newPrintStream(testOutputStream);
originalOut = System.out;
originalErr = System.err;System.setOut(testOuputPrintStream);System.setErr(testOuputPrintStream);}/**
* Restore the System.out and System.err streams to their original state.
* Close the temporary stream created for the purpose of redirecting I/O
* while the initializing is going on.
*/privatestaticvoid restoreStreams(){System.setOut(originalOut);System.setErr(originalErr);
testOuputPrintStream.close();try{
testOutputStream.close();}catch(IOException e){Logger.getLogger(JBossUtil.class.getName()).info("Unable to close testoutstream");}}/**
* This method starts and initializes the embeddable container. We do not
* offer a method to properly clean up the container since this is really
* meant for testing only.
*
* This method may freely be called as often as you'd like since it lazily
* initializes the container only once.
*/publicstaticvoid startDeployer(){if(!initialized){
redirectStreams();
EJB3StandaloneBootstrap.boot(null);
EJB3StandaloneBootstrap.scanClasspath();
initialized = true;
restoreStreams();}}/**
* This is for symmetry. Given how we are using this class, there's little
* need to actually shutdown the container since we run a quick application
* and then stop the JVM.
*/publicstaticvoid shutdownDeployer(){
EJB3StandaloneBootstrap.shutdown();}privatestaticInitialContext getContext(){/**
* We only keep one context around, so lazily initialize it
*/if(ctx == null){try{
ctx = newInitialContext();}catch(NamingException e){thrownewRuntimeException("Unable to get initial context", e);}}return ctx;}/**
* The lookup method on InitialContext returns Object. This simple wrapper
* asks first for the expected type and the for the name to find. It gets
* the name out of JNDI and performs a simple type-check. It then casts to
* the type provided as the first parameter.
*
* This isn't strictly correct since the cast uses the expression (T), where
* T is the generic parameter and the type is erased at run-time. However,
* since we first perform a type check, we know this cast is safe. The -at-
* SuppressWarnings lets the Java Compiler know that we think we know what
* we are doing.
*
* @param <T>
* Type type provided as the first parameter
* @param clazz
* The type to cast to upon return
* @param name
* The name to find in Jndi, e.g. XxxDao/local or, XxxDao/Remote
* @return Something out of Jndi cast to the type provided as the first
* parameter.
*/
@SuppressWarnings("unchecked")publicstatic<T> T lookup(Class<T> clazz, String name){finalInitialContext ctx = getContext();/**
* Perform the lookup, verify that it is type-compatible with clazz and
* cast the return type (using the erased type because that's all we
* have) so the client does not need to perform the cast.
*/try{finalObject object = ctx.lookup(name);if(clazz.isAssignableFrom(object.getClass())){return(T) object;}else{thrownewRuntimeException(String.format("Class found: %s cannot be assigned to type: %s",
object.getClass(), clazz));}}catch(NamingException e){thrownewRuntimeException(String.format("Unable to find ejb for %s", clazz.getName()), e);}}}
EqualsUtil.java
packageutil;/**
* We typically need to compare two object and also perform null checking. This
* class provides a simple wrapper to accomplish doing so.
*/publicclass EqualsUtil {private EqualsUtil(){// I'm a utility class, do not instantiate me}publicstaticboolean equals(finalObject lhs, finalObject rhs){return lhs == null&& rhs == null
|| (lhs != null&& rhs != null&& lhs.equals(rhs));}}
To create this file,
Select your src directory
Right-click and select New::Class
For Class Name enter JBossUtil
For Package enter util
Click Finish
Type or copy the code from above into the new file then save it by pressing ctrl-s
Using JUnit
Now we need to enter basic system setup and then execute a test. The following unit test performs basic setup and initialization, looks up a session bean and sends it a message.
To create this test:
Select your test source folder
Right-click, select New:Class
Enter HelloWorldServiceImplTest for the name
Enter ervice.impl for the package
Click Finish
Copy the text below into the file (replacing the entire contents)
Save the file
packageservice.impl;importorg.junit.Before;importorg.junit.BeforeClass;importorg.junit.Test;importservice.HelloWorldService;importutil.JBossUtil;publicclass HelloWorldServiceImplTest {private HelloWorldService service;
@BeforeClass
publicstaticvoid startDeployer(){// Note, we could stop the deployer when we are done but we do not since// the JVM will shut down and stop the deployer for us.
JBossUtil.startDeployer();}
@Before
publicvoid lookupService(){
service = JBossUtil.lookup(HelloWorldService.class,
"HelloWorldServiceImpl/local");}
@Test
publicvoid sayHello(){
service.sayHelloTo("Brett");}}
Execute your "Unit Test"
Run this JUnit "test" and make sure it runs successfully. (Right-click on the class, select Run As:JUnit Test.
You will see the following output:
Hello to Brett
Note that this example produces output to the console. This example service is not really very testable. How might you "fix" this?
Nice
Congratulations, you've created your first EJB3 Session bean and executed it.
Exercises
Track Usage
Add support in your service implementation class to track the number of times the service has been used. Add two support methods to get the count of the number of times the service has been used and a second method to reset the count back to zero.
Keep a Historical Record
Remember the names of all the people your service has tracked avoiding duplicates. Add two methods to your service: one to report all the names your service has printed, one to clear the list of names.
Note, you'll either want to use synchronized for this assignment or better yet, look at the package java.util.concurrent and pay attention to the class CopyOnWriteArraySet.
Advanced
Update your service to keep track of the number of times each name has been used. You might want to have a look at the class java.util.concurrent.ConcurrentHashmap.
Getting Started with the JBoss Embedded Container
This tutorial is a derivative (simplification really) of work done by JBoss. For the original material, look here. This tutorial's goals are:
Most of this tutorial is about configuration and utility methods. We will use those items for the remainder of the tutorials using the Embeddable EJB3 Container.
Background
A Session Bean provides an interface to a service, wraps business logic or might simply act to hide the details of using JPA. The design pattern that comes to mind is facade.Creating a Session Bean using EJB3 is comparatively painless compared to previous versions of the EJB specification. In the simplest configuration, a session bean only needs to implement an interface and then be denoted as a session bean with meta-information. EJB3 uses configuration by exception to keep the requirements to a minimum.
EJB meta-information comes in two forms: Annotations or XML files. As with JPA, XML files take precedence over annotations. These tutorials use annotations.
Download and Install
This tutorial assumes you already have Eclipse 3.2 and a Java 5 JDK installed. If not, please review this.Download
Download the JBoss EJB3 Embeddable Container from here. The version used for this tutorial is jboss-EJB-3.0_Embeddable_ALPHA_9.zip.Install
Extract this zip file anywhere, for this tutorial I used C:\libs, which makes the following directory: C:\libs\jboss-EJB-3.0_Embeddable_ALPHA_9You'll find the following directories under the install directory:
We'll be using the files from conf and lib.
Create and Configure the Eclipse Project
Define User Library
We need to add several jar files to the class path. We created a user library in these steps and we're going to do the same thing with a different set of jar files.Create User Library
Add Jars to User Library
Now we need to add all of the jar files from the lib directory under the JBoss Embeddable container directory we unzipped earlier:
You need to add all of the jar files from the lib directory under the JBoss Embeddable container directory. If you used the same directory as the tutorial, then you need add all of the jar files from C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/lib/ (there are 5). Here is the complete list of jar files:
Create and Setup Project
For the rests of this tutorial, when you see <project> replace it with Ejb3Tutorial1.Create the Project
Edit your project properties
Now that we have created a user library, we can add that user library to our project:Setup the configuration files
The JBoss Embeddable container looks for several files in the classpath. To keep all of these in one place, we'll add another source directory to our project and then import several files into that directory.Add Resource Adapter Archive(RAR)
The Java Connector system defines Resource Adapter Archive files (RAR files). We need to add a few RAR files into the class path. We will import two more files into the conf directory:Create a jndi.properties file
Note, depending on the version of the embeddable container you download, you might already have a file called jndi.properties. If you do, skip to the next section.Create a persistence.xml
This example presents a utility class we'll be using later. The container needs a persistence.xml file to operate. This file must be found under a META-INF directory somewhere in the classpath or the embeddable container will not start. The file's name is persistence.xml with a lower-case 'p'. On a Unix system, this will make a difference. On a PC, this won't make a difference and it is one of those things that might work on your machine but not on the linux build box.persistence.xml
Create a Local Stateless Session Bean
Now that we have all the preliminary setup of the environment we next need to create a session bean.The Session Bean
The basic requirement for a session bean is that it must implement an interface and be either annotated with @Stateless or be registered in an XML file. We are sticking strictly to using annotations. The annotation goes on the class and not the interface, so here's the interface.The Interface
First we create a session bean. Here is one such example:
To create this file,
The Session Bean
Next, we need to create a session bean. Here's the code for it:
Notice that this class has the @Stateless annotation. The container will find this class and register it automatically under JNDI using the (non-package qualifited) class name plus "/local". In this example, that means we'll need to lookup "HelloWorldServiceImpl/local".
This class is obviously stateless because of the annotation. This is the default behavior. However, using the annotation will get it automatically available from JNDI. (We could put this information in an XML file and get the same results.)
This class is also local. By default, session beans are local (no RMI-IIOP to call them) unless:
If you still want a local session bean where there is more than one interface, you can use @Local.
To create this file:
Container Initialization and Session Bean Lookup
There's a bit of boilerplate code to get the JBoss EJB3 Embeddable Container initialized before we can look up our session bean. For now we'll just use this code so we won't describe it in any detail here.By the way, I recommend you cut and past this code rather than type it.
JBossUtil.java
EqualsUtil.java
To create this file,
Using JUnit
Now we need to enter basic system setup and then execute a test. The following unit test performs basic setup and initialization, looks up a session bean and sends it a message.To create this test:
Execute your "Unit Test"
Run this JUnit "test" and make sure it runs successfully. (Right-click on the class, select Run As:JUnit Test.You will see the following output:
Note that this example produces output to the console. This example service is not really very testable. How might you "fix" this?
Nice
Congratulations, you've created your first EJB3 Session bean and executed it.Exercises
Track UsageAdd support in your service implementation class to track the number of times the service has been used. Add two support methods to get the count of the number of times the service has been used and a second method to reset the count back to zero.
Keep a Historical Record
Remember the names of all the people your service has tracked avoiding duplicates. Add two methods to your service: one to report all the names your service has printed, one to clear the list of names.
Note, you'll either want to use synchronized for this assignment or better yet, look at the package java.util.concurrent and pay attention to the class CopyOnWriteArraySet.
Advanced
Update your service to keep track of the number of times each name has been used. You might want to have a look at the class java.util.concurrent.ConcurrentHashmap.
<--Back